//
//  SASLPrep.swift
//  PostgresClientKit
//
//  Copyright 2020 David Pitfield and the PostgresClientKit contributors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

internal extension String {
    
    /// Gets the SASLPrep (RFC 4013) transformation of this `String`.
    ///
    /// - Parameter stringType: whether to treat this string as a "stored string"
    ///     or a "query" (see RFC 3454)
    ///
    /// - Throws: `String.SASLPrepError` if the operation fails
    /// - Returns: the transformed string
    func saslPrep(stringType: SASLPrepStringType) throws -> String {
        
        // Section 2.1 Mapping
        var codeUnits = unicodeScalars.map { $0.isNonAsciiSpaceCharacter ? " " : $0 }
        codeUnits = codeUnits.compactMap { $0.isCommonlyMappedToNothing ? nil : $0 }
        
        // Section 2.2 Normalization
        codeUnits = [UnicodeScalarView.Element](
            String(UnicodeScalarView(codeUnits))
                .precomposedStringWithCompatibilityMapping
                .unicodeScalars)
        
        // Section 2.3 Prohibited output
        for codeUnit in codeUnits {
            if codeUnit.isProhibitedOutput {
                throw SASLPrepError.prohibitedOutput(scalar: codeUnit)
            }
        }
        
        // Section 2.4 Bidirectional characters
        let containsRandALCat = codeUnits.contains { $0.isRandALCat }
        let containsLCat = codeUnits.contains { $0.isLCat }
        
        if containsRandALCat && containsLCat {
            throw SASLPrepError.prohibitedBidirectionalString(string: self)
        }
        
        if containsRandALCat {
            if !codeUnits.first!.isRandALCat ||
                !codeUnits.last!.isRandALCat {
                throw SASLPrepError.prohibitedBidirectionalString(string: self)
            }
        }
        
        // Section 2.5 Unassigned code points
        if stringType == .storedString {
            for codeUnit in codeUnits {
                if codeUnit.isUnassignedCodePoint {
                    throw SASLPrepError.unassignedCodePoint(scalar: codeUnit)
                }
            }
        }
        
        return String(UnicodeScalarView(codeUnits))
    }
    
    enum SASLPrepStringType {
        case storedString
        case query
    }
    
    enum SASLPrepError: Error {
        case prohibitedOutput(scalar: Unicode.Scalar)
        case prohibitedBidirectionalString(string: String)
        case unassignedCodePoint(scalar: Unicode.Scalar)
    }
}

fileprivate extension Unicode.Scalar {
    
    // The Unicode code point tables are from the Postgres server's SASLPrep implementation.
    // https://doxygen.postgresql.org/saslprep_8c_source.html
        
    // RFC 3454 table C.1.2
    var isNonAsciiSpaceCharacter: Bool {
        
        let ranges: [ClosedRange<UInt32>] = [
            0x00A0...0x00A0,
            0x1680...0x1680,
            0x2000...0x200B,
            0x202F...0x202F,
            0x205F...0x205F,
            0x3000...0x3000 ]
        
        return ranges.contains { $0.contains(value) }
    }
    
    // RFC 3454 table B.1
    var isCommonlyMappedToNothing: Bool {
        
        let ranges: [ClosedRange<UInt32>] = [
            0x00AD...0x00AD,
            0x034F...0x034F,
            0x1806...0x1806,
            0x180B...0x180D,
            0x200B...0x200D,
            0x2060...0x2060,
            0xFE00...0xFE0F,
            0xFEFF...0xFEFF ]
        
        return ranges.contains { $0.contains(value) }
    }
    
    // RFC 3454 tables:
    //   C.1.2 Non-ASCII space characters
    //   C.2.1 ASCII control characters
    //   C.2.2 Non-ASCII control characters
    //   C.3 Private Use characters
    //   C.4 Non-character code points
    //   C.5 Surrogate code points
    //   C.6 Inappropriate for plain text characters
    //   C.7 Inappropriate for canonical representation characters
    //   C.8 Change display properties or deprecated characters
    //   C.9 Tagging characters
    var isProhibitedOutput: Bool {
        
        let ranges: [ClosedRange<UInt32>] = [
            0x0000...0x001F,         // C.2.1
            0x007F...0x00A0,         // C.1.2...C.2.1...C.2.2
            0x0340...0x0341,         // C.8
            0x06DD...0x06DD,         // C.2.2
            0x070F...0x070F,         // C.2.2
            0x1680...0x1680,         // C.1.2
            0x180E...0x180E,         // C.2.2
            0x2000...0x200F,         // C.1.2...C.2.2...C.8
            0x2028...0x202F,         // C.1.2...C.2.2...C.8
            0x205F...0x2063,         // C.1.2...C.2.2
            0x206A...0x206F,         // C.2.2...C.8
            0x2FF0...0x2FFB,         // C.7
            0x3000...0x3000,         // C.1.2
            0xD800...0xF8FF,         // C.3...C.5
            0xFDD0...0xFDEF,         // C.4
            0xFEFF...0xFEFF,         // C.2.2
            0xFFF9...0xFFFF,         // C.2.2...C.4...C.6
            0x1D173...0x1D17A,       // C.2.2
            0x1FFFE...0x1FFFF,       // C.4
            0x2FFFE...0x2FFFF,       // C.4
            0x3FFFE...0x3FFFF,       // C.4
            0x4FFFE...0x4FFFF,       // C.4
            0x5FFFE...0x5FFFF,       // C.4
            0x6FFFE...0x6FFFF,       // C.4
            0x7FFFE...0x7FFFF,       // C.4
            0x8FFFE...0x8FFFF,       // C.4
            0x9FFFE...0x9FFFF,       // C.4
            0xAFFFE...0xAFFFF,       // C.4
            0xBFFFE...0xBFFFF,       // C.4
            0xCFFFE...0xCFFFF,       // C.4
            0xDFFFE...0xDFFFF,       // C.4
            0xE0001...0xE0001,       // C.9
            0xE0020...0xE007F,       // C.9
            0xEFFFE...0xEFFFF,       // C.4
            0xF0000...0xFFFFF,       // C.3...C.4
            0x100000...0x10FFFF ]    // C.3...C.4
        
        return ranges.contains { $0.contains(value) }
    }
    
    // RFC 3454 table A.1
    var isUnassignedCodePoint: Bool {
        
        let ranges: [ClosedRange<UInt32>] = [
            0x0221...0x0221,
            0x0234...0x024F,
            0x02AE...0x02AF,
            0x02EF...0x02FF,
            0x0350...0x035F,
            0x0370...0x0373,
            0x0376...0x0379,
            0x037B...0x037D,
            0x037F...0x0383,
            0x038B...0x038B,
            0x038D...0x038D,
            0x03A2...0x03A2,
            0x03CF...0x03CF,
            0x03F7...0x03FF,
            0x0487...0x0487,
            0x04CF...0x04CF,
            0x04F6...0x04F7,
            0x04FA...0x04FF,
            0x0510...0x0530,
            0x0557...0x0558,
            0x0560...0x0560,
            0x0588...0x0588,
            0x058B...0x0590,
            0x05A2...0x05A2,
            0x05BA...0x05BA,
            0x05C5...0x05CF,
            0x05EB...0x05EF,
            0x05F5...0x060B,
            0x060D...0x061A,
            0x061C...0x061E,
            0x0620...0x0620,
            0x063B...0x063F,
            0x0656...0x065F,
            0x06EE...0x06EF,
            0x06FF...0x06FF,
            0x070E...0x070E,
            0x072D...0x072F,
            0x074B...0x077F,
            0x07B2...0x0900,
            0x0904...0x0904,
            0x093A...0x093B,
            0x094E...0x094F,
            0x0955...0x0957,
            0x0971...0x0980,
            0x0984...0x0984,
            0x098D...0x098E,
            0x0991...0x0992,
            0x09A9...0x09A9,
            0x09B1...0x09B1,
            0x09B3...0x09B5,
            0x09BA...0x09BB,
            0x09BD...0x09BD,
            0x09C5...0x09C6,
            0x09C9...0x09CA,
            0x09CE...0x09D6,
            0x09D8...0x09DB,
            0x09DE...0x09DE,
            0x09E4...0x09E5,
            0x09FB...0x0A01,
            0x0A03...0x0A04,
            0x0A0B...0x0A0E,
            0x0A11...0x0A12,
            0x0A29...0x0A29,
            0x0A31...0x0A31,
            0x0A34...0x0A34,
            0x0A37...0x0A37,
            0x0A3A...0x0A3B,
            0x0A3D...0x0A3D,
            0x0A43...0x0A46,
            0x0A49...0x0A4A,
            0x0A4E...0x0A58,
            0x0A5D...0x0A5D,
            0x0A5F...0x0A65,
            0x0A75...0x0A80,
            0x0A84...0x0A84,
            0x0A8C...0x0A8C,
            0x0A8E...0x0A8E,
            0x0A92...0x0A92,
            0x0AA9...0x0AA9,
            0x0AB1...0x0AB1,
            0x0AB4...0x0AB4,
            0x0ABA...0x0ABB,
            0x0AC6...0x0AC6,
            0x0ACA...0x0ACA,
            0x0ACE...0x0ACF,
            0x0AD1...0x0ADF,
            0x0AE1...0x0AE5,
            0x0AF0...0x0B00,
            0x0B04...0x0B04,
            0x0B0D...0x0B0E,
            0x0B11...0x0B12,
            0x0B29...0x0B29,
            0x0B31...0x0B31,
            0x0B34...0x0B35,
            0x0B3A...0x0B3B,
            0x0B44...0x0B46,
            0x0B49...0x0B4A,
            0x0B4E...0x0B55,
            0x0B58...0x0B5B,
            0x0B5E...0x0B5E,
            0x0B62...0x0B65,
            0x0B71...0x0B81,
            0x0B84...0x0B84,
            0x0B8B...0x0B8D,
            0x0B91...0x0B91,
            0x0B96...0x0B98,
            0x0B9B...0x0B9B,
            0x0B9D...0x0B9D,
            0x0BA0...0x0BA2,
            0x0BA5...0x0BA7,
            0x0BAB...0x0BAD,
            0x0BB6...0x0BB6,
            0x0BBA...0x0BBD,
            0x0BC3...0x0BC5,
            0x0BC9...0x0BC9,
            0x0BCE...0x0BD6,
            0x0BD8...0x0BE6,
            0x0BF3...0x0C00,
            0x0C04...0x0C04,
            0x0C0D...0x0C0D,
            0x0C11...0x0C11,
            0x0C29...0x0C29,
            0x0C34...0x0C34,
            0x0C3A...0x0C3D,
            0x0C45...0x0C45,
            0x0C49...0x0C49,
            0x0C4E...0x0C54,
            0x0C57...0x0C5F,
            0x0C62...0x0C65,
            0x0C70...0x0C81,
            0x0C84...0x0C84,
            0x0C8D...0x0C8D,
            0x0C91...0x0C91,
            0x0CA9...0x0CA9,
            0x0CB4...0x0CB4,
            0x0CBA...0x0CBD,
            0x0CC5...0x0CC5,
            0x0CC9...0x0CC9,
            0x0CCE...0x0CD4,
            0x0CD7...0x0CDD,
            0x0CDF...0x0CDF,
            0x0CE2...0x0CE5,
            0x0CF0...0x0D01,
            0x0D04...0x0D04,
            0x0D0D...0x0D0D,
            0x0D11...0x0D11,
            0x0D29...0x0D29,
            0x0D3A...0x0D3D,
            0x0D44...0x0D45,
            0x0D49...0x0D49,
            0x0D4E...0x0D56,
            0x0D58...0x0D5F,
            0x0D62...0x0D65,
            0x0D70...0x0D81,
            0x0D84...0x0D84,
            0x0D97...0x0D99,
            0x0DB2...0x0DB2,
            0x0DBC...0x0DBC,
            0x0DBE...0x0DBF,
            0x0DC7...0x0DC9,
            0x0DCB...0x0DCE,
            0x0DD5...0x0DD5,
            0x0DD7...0x0DD7,
            0x0DE0...0x0DF1,
            0x0DF5...0x0E00,
            0x0E3B...0x0E3E,
            0x0E5C...0x0E80,
            0x0E83...0x0E83,
            0x0E85...0x0E86,
            0x0E89...0x0E89,
            0x0E8B...0x0E8C,
            0x0E8E...0x0E93,
            0x0E98...0x0E98,
            0x0EA0...0x0EA0,
            0x0EA4...0x0EA4,
            0x0EA6...0x0EA6,
            0x0EA8...0x0EA9,
            0x0EAC...0x0EAC,
            0x0EBA...0x0EBA,
            0x0EBE...0x0EBF,
            0x0EC5...0x0EC5,
            0x0EC7...0x0EC7,
            0x0ECE...0x0ECF,
            0x0EDA...0x0EDB,
            0x0EDE...0x0EFF,
            0x0F48...0x0F48,
            0x0F6B...0x0F70,
            0x0F8C...0x0F8F,
            0x0F98...0x0F98,
            0x0FBD...0x0FBD,
            0x0FCD...0x0FCE,
            0x0FD0...0x0FFF,
            0x1022...0x1022,
            0x1028...0x1028,
            0x102B...0x102B,
            0x1033...0x1035,
            0x103A...0x103F,
            0x105A...0x109F,
            0x10C6...0x10CF,
            0x10F9...0x10FA,
            0x10FC...0x10FF,
            0x115A...0x115E,
            0x11A3...0x11A7,
            0x11FA...0x11FF,
            0x1207...0x1207,
            0x1247...0x1247,
            0x1249...0x1249,
            0x124E...0x124F,
            0x1257...0x1257,
            0x1259...0x1259,
            0x125E...0x125F,
            0x1287...0x1287,
            0x1289...0x1289,
            0x128E...0x128F,
            0x12AF...0x12AF,
            0x12B1...0x12B1,
            0x12B6...0x12B7,
            0x12BF...0x12BF,
            0x12C1...0x12C1,
            0x12C6...0x12C7,
            0x12CF...0x12CF,
            0x12D7...0x12D7,
            0x12EF...0x12EF,
            0x130F...0x130F,
            0x1311...0x1311,
            0x1316...0x1317,
            0x131F...0x131F,
            0x1347...0x1347,
            0x135B...0x1360,
            0x137D...0x139F,
            0x13F5...0x1400,
            0x1677...0x167F,
            0x169D...0x169F,
            0x16F1...0x16FF,
            0x170D...0x170D,
            0x1715...0x171F,
            0x1737...0x173F,
            0x1754...0x175F,
            0x176D...0x176D,
            0x1771...0x1771,
            0x1774...0x177F,
            0x17DD...0x17DF,
            0x17EA...0x17FF,
            0x180F...0x180F,
            0x181A...0x181F,
            0x1878...0x187F,
            0x18AA...0x1DFF,
            0x1E9C...0x1E9F,
            0x1EFA...0x1EFF,
            0x1F16...0x1F17,
            0x1F1E...0x1F1F,
            0x1F46...0x1F47,
            0x1F4E...0x1F4F,
            0x1F58...0x1F58,
            0x1F5A...0x1F5A,
            0x1F5C...0x1F5C,
            0x1F5E...0x1F5E,
            0x1F7E...0x1F7F,
            0x1FB5...0x1FB5,
            0x1FC5...0x1FC5,
            0x1FD4...0x1FD5,
            0x1FDC...0x1FDC,
            0x1FF0...0x1FF1,
            0x1FF5...0x1FF5,
            0x1FFF...0x1FFF,
            0x2053...0x2056,
            0x2058...0x205E,
            0x2064...0x2069,
            0x2072...0x2073,
            0x208F...0x209F,
            0x20B2...0x20CF,
            0x20EB...0x20FF,
            0x213B...0x213C,
            0x214C...0x2152,
            0x2184...0x218F,
            0x23CF...0x23FF,
            0x2427...0x243F,
            0x244B...0x245F,
            0x24FF...0x24FF,
            0x2614...0x2615,
            0x2618...0x2618,
            0x267E...0x267F,
            0x268A...0x2700,
            0x2705...0x2705,
            0x270A...0x270B,
            0x2728...0x2728,
            0x274C...0x274C,
            0x274E...0x274E,
            0x2753...0x2755,
            0x2757...0x2757,
            0x275F...0x2760,
            0x2795...0x2797,
            0x27B0...0x27B0,
            0x27BF...0x27CF,
            0x27EC...0x27EF,
            0x2B00...0x2E7F,
            0x2E9A...0x2E9A,
            0x2EF4...0x2EFF,
            0x2FD6...0x2FEF,
            0x2FFC...0x2FFF,
            0x3040...0x3040,
            0x3097...0x3098,
            0x3100...0x3104,
            0x312D...0x3130,
            0x318F...0x318F,
            0x31B8...0x31EF,
            0x321D...0x321F,
            0x3244...0x3250,
            0x327C...0x327E,
            0x32CC...0x32CF,
            0x32FF...0x32FF,
            0x3377...0x337A,
            0x33DE...0x33DF,
            0x33FF...0x33FF,
            0x4DB6...0x4DFF,
            0x9FA6...0x9FFF,
            0xA48D...0xA48F,
            0xA4C7...0xABFF,
            0xD7A4...0xD7FF,
            0xFA2E...0xFA2F,
            0xFA6B...0xFAFF,
            0xFB07...0xFB12,
            0xFB18...0xFB1C,
            0xFB37...0xFB37,
            0xFB3D...0xFB3D,
            0xFB3F...0xFB3F,
            0xFB42...0xFB42,
            0xFB45...0xFB45,
            0xFBB2...0xFBD2,
            0xFD40...0xFD4F,
            0xFD90...0xFD91,
            0xFDC8...0xFDCF,
            0xFDFD...0xFDFF,
            0xFE10...0xFE1F,
            0xFE24...0xFE2F,
            0xFE47...0xFE48,
            0xFE53...0xFE53,
            0xFE67...0xFE67,
            0xFE6C...0xFE6F,
            0xFE75...0xFE75,
            0xFEFD...0xFEFE,
            0xFF00...0xFF00,
            0xFFBF...0xFFC1,
            0xFFC8...0xFFC9,
            0xFFD0...0xFFD1,
            0xFFD8...0xFFD9,
            0xFFDD...0xFFDF,
            0xFFE7...0xFFE7,
            0xFFEF...0xFFF8,
            0x10000...0x102FF,
            0x1031F...0x1031F,
            0x10324...0x1032F,
            0x1034B...0x103FF,
            0x10426...0x10427,
            0x1044E...0x1CFFF,
            0x1D0F6...0x1D0FF,
            0x1D127...0x1D129,
            0x1D1DE...0x1D3FF,
            0x1D455...0x1D455,
            0x1D49D...0x1D49D,
            0x1D4A0...0x1D4A1,
            0x1D4A3...0x1D4A4,
            0x1D4A7...0x1D4A8,
            0x1D4AD...0x1D4AD,
            0x1D4BA...0x1D4BA,
            0x1D4BC...0x1D4BC,
            0x1D4C1...0x1D4C1,
            0x1D4C4...0x1D4C4,
            0x1D506...0x1D506,
            0x1D50B...0x1D50C,
            0x1D515...0x1D515,
            0x1D51D...0x1D51D,
            0x1D53A...0x1D53A,
            0x1D53F...0x1D53F,
            0x1D545...0x1D545,
            0x1D547...0x1D549,
            0x1D551...0x1D551,
            0x1D6A4...0x1D6A7,
            0x1D7CA...0x1D7CD,
            0x1D800...0x1FFFD,
            0x2A6D7...0x2F7FF,
            0x2FA1E...0x2FFFD,
            0x30000...0x3FFFD,
            0x40000...0x4FFFD,
            0x50000...0x5FFFD,
            0x60000...0x6FFFD,
            0x70000...0x7FFFD,
            0x80000...0x8FFFD,
            0x90000...0x9FFFD,
            0xA0000...0xAFFFD,
            0xB0000...0xBFFFD,
            0xC0000...0xCFFFD,
            0xD0000...0xDFFFD,
            0xE0000...0xE0000,
            0xE0002...0xE001F,
            0xE0080...0xEFFFD
        ]
        
        return ranges.contains { $0.contains(value) }
    }
    
    // RFC 3454 table D.1
    var isRandALCat: Bool {
        
        let ranges: [ClosedRange<UInt32>] = [
            0x05BE...0x05BE,
            0x05C0...0x05C0,
            0x05C3...0x05C3,
            0x05D0...0x05EA,
            0x05F0...0x05F4,
            0x061B...0x061B,
            0x061F...0x061F,
            0x0621...0x063A,
            0x0640...0x064A,
            0x066D...0x066F,
            0x0671...0x06D5,
            0x06DD...0x06DD,
            0x06E5...0x06E6,
            0x06FA...0x06FE,
            0x0700...0x070D,
            0x0710...0x0710,
            0x0712...0x072C,
            0x0780...0x07A5,
            0x07B1...0x07B1,
            0x200F...0x200F,
            0xFB1D...0xFB1D,
            0xFB1F...0xFB28,
            0xFB2A...0xFB36,
            0xFB38...0xFB3C,
            0xFB3E...0xFB3E,
            0xFB40...0xFB41,
            0xFB43...0xFB44,
            0xFB46...0xFBB1,
            0xFBD3...0xFD3D,
            0xFD50...0xFD8F,
            0xFD92...0xFDC7,
            0xFDF0...0xFDFC,
            0xFE70...0xFE74,
            0xFE76...0xFEFC ]
        
        return ranges.contains { $0.contains(value) }
    }
    
    // RFC 3454 table D.2
    var isLCat: Bool {
        
        let ranges: [ClosedRange<UInt32>] = [
            0x0041...0x005A,
            0x0061...0x007A,
            0x00AA...0x00AA,
            0x00B5...0x00B5,
            0x00BA...0x00BA,
            0x00C0...0x00D6,
            0x00D8...0x00F6,
            0x00F8...0x0220,
            0x0222...0x0233,
            0x0250...0x02AD,
            0x02B0...0x02B8,
            0x02BB...0x02C1,
            0x02D0...0x02D1,
            0x02E0...0x02E4,
            0x02EE...0x02EE,
            0x037A...0x037A,
            0x0386...0x0386,
            0x0388...0x038A,
            0x038C...0x038C,
            0x038E...0x03A1,
            0x03A3...0x03CE,
            0x03D0...0x03F5,
            0x0400...0x0482,
            0x048A...0x04CE,
            0x04D0...0x04F5,
            0x04F8...0x04F9,
            0x0500...0x050F,
            0x0531...0x0556,
            0x0559...0x055F,
            0x0561...0x0587,
            0x0589...0x0589,
            0x0903...0x0903,
            0x0905...0x0939,
            0x093D...0x0940,
            0x0949...0x094C,
            0x0950...0x0950,
            0x0958...0x0961,
            0x0964...0x0970,
            0x0982...0x0983,
            0x0985...0x098C,
            0x098F...0x0990,
            0x0993...0x09A8,
            0x09AA...0x09B0,
            0x09B2...0x09B2,
            0x09B6...0x09B9,
            0x09BE...0x09C0,
            0x09C7...0x09C8,
            0x09CB...0x09CC,
            0x09D7...0x09D7,
            0x09DC...0x09DD,
            0x09DF...0x09E1,
            0x09E6...0x09F1,
            0x09F4...0x09FA,
            0x0A05...0x0A0A,
            0x0A0F...0x0A10,
            0x0A13...0x0A28,
            0x0A2A...0x0A30,
            0x0A32...0x0A33,
            0x0A35...0x0A36,
            0x0A38...0x0A39,
            0x0A3E...0x0A40,
            0x0A59...0x0A5C,
            0x0A5E...0x0A5E,
            0x0A66...0x0A6F,
            0x0A72...0x0A74,
            0x0A83...0x0A83,
            0x0A85...0x0A8B,
            0x0A8D...0x0A8D,
            0x0A8F...0x0A91,
            0x0A93...0x0AA8,
            0x0AAA...0x0AB0,
            0x0AB2...0x0AB3,
            0x0AB5...0x0AB9,
            0x0ABD...0x0AC0,
            0x0AC9...0x0AC9,
            0x0ACB...0x0ACC,
            0x0AD0...0x0AD0,
            0x0AE0...0x0AE0,
            0x0AE6...0x0AEF,
            0x0B02...0x0B03,
            0x0B05...0x0B0C,
            0x0B0F...0x0B10,
            0x0B13...0x0B28,
            0x0B2A...0x0B30,
            0x0B32...0x0B33,
            0x0B36...0x0B39,
            0x0B3D...0x0B3E,
            0x0B40...0x0B40,
            0x0B47...0x0B48,
            0x0B4B...0x0B4C,
            0x0B57...0x0B57,
            0x0B5C...0x0B5D,
            0x0B5F...0x0B61,
            0x0B66...0x0B70,
            0x0B83...0x0B83,
            0x0B85...0x0B8A,
            0x0B8E...0x0B90,
            0x0B92...0x0B95,
            0x0B99...0x0B9A,
            0x0B9C...0x0B9C,
            0x0B9E...0x0B9F,
            0x0BA3...0x0BA4,
            0x0BA8...0x0BAA,
            0x0BAE...0x0BB5,
            0x0BB7...0x0BB9,
            0x0BBE...0x0BBF,
            0x0BC1...0x0BC2,
            0x0BC6...0x0BC8,
            0x0BCA...0x0BCC,
            0x0BD7...0x0BD7,
            0x0BE7...0x0BF2,
            0x0C01...0x0C03,
            0x0C05...0x0C0C,
            0x0C0E...0x0C10,
            0x0C12...0x0C28,
            0x0C2A...0x0C33,
            0x0C35...0x0C39,
            0x0C41...0x0C44,
            0x0C60...0x0C61,
            0x0C66...0x0C6F,
            0x0C82...0x0C83,
            0x0C85...0x0C8C,
            0x0C8E...0x0C90,
            0x0C92...0x0CA8,
            0x0CAA...0x0CB3,
            0x0CB5...0x0CB9,
            0x0CBE...0x0CBE,
            0x0CC0...0x0CC4,
            0x0CC7...0x0CC8,
            0x0CCA...0x0CCB,
            0x0CD5...0x0CD6,
            0x0CDE...0x0CDE,
            0x0CE0...0x0CE1,
            0x0CE6...0x0CEF,
            0x0D02...0x0D03,
            0x0D05...0x0D0C,
            0x0D0E...0x0D10,
            0x0D12...0x0D28,
            0x0D2A...0x0D39,
            0x0D3E...0x0D40,
            0x0D46...0x0D48,
            0x0D4A...0x0D4C,
            0x0D57...0x0D57,
            0x0D60...0x0D61,
            0x0D66...0x0D6F,
            0x0D82...0x0D83,
            0x0D85...0x0D96,
            0x0D9A...0x0DB1,
            0x0DB3...0x0DBB,
            0x0DBD...0x0DBD,
            0x0DC0...0x0DC6,
            0x0DCF...0x0DD1,
            0x0DD8...0x0DDF,
            0x0DF2...0x0DF4,
            0x0E01...0x0E30,
            0x0E32...0x0E33,
            0x0E40...0x0E46,
            0x0E4F...0x0E5B,
            0x0E81...0x0E82,
            0x0E84...0x0E84,
            0x0E87...0x0E88,
            0x0E8A...0x0E8A,
            0x0E8D...0x0E8D,
            0x0E94...0x0E97,
            0x0E99...0x0E9F,
            0x0EA1...0x0EA3,
            0x0EA5...0x0EA5,
            0x0EA7...0x0EA7,
            0x0EAA...0x0EAB,
            0x0EAD...0x0EB0,
            0x0EB2...0x0EB3,
            0x0EBD...0x0EBD,
            0x0EC0...0x0EC4,
            0x0EC6...0x0EC6,
            0x0ED0...0x0ED9,
            0x0EDC...0x0EDD,
            0x0F00...0x0F17,
            0x0F1A...0x0F34,
            0x0F36...0x0F36,
            0x0F38...0x0F38,
            0x0F3E...0x0F47,
            0x0F49...0x0F6A,
            0x0F7F...0x0F7F,
            0x0F85...0x0F85,
            0x0F88...0x0F8B,
            0x0FBE...0x0FC5,
            0x0FC7...0x0FCC,
            0x0FCF...0x0FCF,
            0x1000...0x1021,
            0x1023...0x1027,
            0x1029...0x102A,
            0x102C...0x102C,
            0x1031...0x1031,
            0x1038...0x1038,
            0x1040...0x1057,
            0x10A0...0x10C5,
            0x10D0...0x10F8,
            0x10FB...0x10FB,
            0x1100...0x1159,
            0x115F...0x11A2,
            0x11A8...0x11F9,
            0x1200...0x1206,
            0x1208...0x1246,
            0x1248...0x1248,
            0x124A...0x124D,
            0x1250...0x1256,
            0x1258...0x1258,
            0x125A...0x125D,
            0x1260...0x1286,
            0x1288...0x1288,
            0x128A...0x128D,
            0x1290...0x12AE,
            0x12B0...0x12B0,
            0x12B2...0x12B5,
            0x12B8...0x12BE,
            0x12C0...0x12C0,
            0x12C2...0x12C5,
            0x12C8...0x12CE,
            0x12D0...0x12D6,
            0x12D8...0x12EE,
            0x12F0...0x130E,
            0x1310...0x1310,
            0x1312...0x1315,
            0x1318...0x131E,
            0x1320...0x1346,
            0x1348...0x135A,
            0x1361...0x137C,
            0x13A0...0x13F4,
            0x1401...0x1676,
            0x1681...0x169A,
            0x16A0...0x16F0,
            0x1700...0x170C,
            0x170E...0x1711,
            0x1720...0x1731,
            0x1735...0x1736,
            0x1740...0x1751,
            0x1760...0x176C,
            0x176E...0x1770,
            0x1780...0x17B6,
            0x17BE...0x17C5,
            0x17C7...0x17C8,
            0x17D4...0x17DA,
            0x17DC...0x17DC,
            0x17E0...0x17E9,
            0x1810...0x1819,
            0x1820...0x1877,
            0x1880...0x18A8,
            0x1E00...0x1E9B,
            0x1EA0...0x1EF9,
            0x1F00...0x1F15,
            0x1F18...0x1F1D,
            0x1F20...0x1F45,
            0x1F48...0x1F4D,
            0x1F50...0x1F57,
            0x1F59...0x1F59,
            0x1F5B...0x1F5B,
            0x1F5D...0x1F5D,
            0x1F5F...0x1F7D,
            0x1F80...0x1FB4,
            0x1FB6...0x1FBC,
            0x1FBE...0x1FBE,
            0x1FC2...0x1FC4,
            0x1FC6...0x1FCC,
            0x1FD0...0x1FD3,
            0x1FD6...0x1FDB,
            0x1FE0...0x1FEC,
            0x1FF2...0x1FF4,
            0x1FF6...0x1FFC,
            0x200E...0x200E,
            0x2071...0x2071,
            0x207F...0x207F,
            0x2102...0x2102,
            0x2107...0x2107,
            0x210A...0x2113,
            0x2115...0x2115,
            0x2119...0x211D,
            0x2124...0x2124,
            0x2126...0x2126,
            0x2128...0x2128,
            0x212A...0x212D,
            0x212F...0x2131,
            0x2133...0x2139,
            0x213D...0x213F,
            0x2145...0x2149,
            0x2160...0x2183,
            0x2336...0x237A,
            0x2395...0x2395,
            0x249C...0x24E9,
            0x3005...0x3007,
            0x3021...0x3029,
            0x3031...0x3035,
            0x3038...0x303C,
            0x3041...0x3096,
            0x309D...0x309F,
            0x30A1...0x30FA,
            0x30FC...0x30FF,
            0x3105...0x312C,
            0x3131...0x318E,
            0x3190...0x31B7,
            0x31F0...0x321C,
            0x3220...0x3243,
            0x3260...0x327B,
            0x327F...0x32B0,
            0x32C0...0x32CB,
            0x32D0...0x32FE,
            0x3300...0x3376,
            0x337B...0x33DD,
            0x33E0...0x33FE,
            0x3400...0x4DB5,
            0x4E00...0x9FA5,
            0xA000...0xA48C,
            0xAC00...0xD7A3,
            0xD800...0xFA2D,
            0xFA30...0xFA6A,
            0xFB00...0xFB06,
            0xFB13...0xFB17,
            0xFF21...0xFF3A,
            0xFF41...0xFF5A,
            0xFF66...0xFFBE,
            0xFFC2...0xFFC7,
            0xFFCA...0xFFCF,
            0xFFD2...0xFFD7,
            0xFFDA...0xFFDC,
            0x10300...0x1031E,
            0x10320...0x10323,
            0x10330...0x1034A,
            0x10400...0x10425,
            0x10428...0x1044D,
            0x1D000...0x1D0F5,
            0x1D100...0x1D126,
            0x1D12A...0x1D166,
            0x1D16A...0x1D172,
            0x1D183...0x1D184,
            0x1D18C...0x1D1A9,
            0x1D1AE...0x1D1DD,
            0x1D400...0x1D454,
            0x1D456...0x1D49C,
            0x1D49E...0x1D49F,
            0x1D4A2...0x1D4A2,
            0x1D4A5...0x1D4A6,
            0x1D4A9...0x1D4AC,
            0x1D4AE...0x1D4B9,
            0x1D4BB...0x1D4BB,
            0x1D4BD...0x1D4C0,
            0x1D4C2...0x1D4C3,
            0x1D4C5...0x1D505,
            0x1D507...0x1D50A,
            0x1D50D...0x1D514,
            0x1D516...0x1D51C,
            0x1D51E...0x1D539,
            0x1D53B...0x1D53E,
            0x1D540...0x1D544,
            0x1D546...0x1D546,
            0x1D54A...0x1D550,
            0x1D552...0x1D6A3,
            0x1D6A8...0x1D7C9,
            0x20000...0x2A6D6,
            0x2F800...0x2FA1D,
            0xF0000...0xFFFFD,
            0x100000...0x10FFFD ]
        
        return ranges.contains { $0.contains(value) }
    }
}

// EOF
